<?php

namespace AgilePayments\Ump;

use AgilePayments\Config;
use AgilePayments\Tool;
use fast\Random;
use think\cache\driver\Redis;

class Service
{
    private $config;

    private $accessToken;

    private $serverBaseUrl = 'https://api-mop.chinaums.com/';
    
    public function __construct(Config $config)
    {
        $this->config = $config;

        $this->appId = $this->config->pay_type_config->app_id;
        $this->appKey = $this->config->pay_type_config->app_key;
        if ($this->accessToken == '') $this->setToken();
    }


    /**
     * @author zhangZC
     * @creationtime 2022年10月28日
     * @desc 设置token
     */
    private function setToken()
    {
        $redis = new Redis(\think\Config::get('cache.redis', []));

        if (!$redis->has('YSAccessToken')) {
            $url = $this->serverBaseUrl . 'v1/token/access';
            $timestamp = date('YmdHis');
            $nonce = Random::numeric('8');
            $signMethod = 'SHA256';
            $signature = $this->Signature($timestamp, $nonce);

            $result =  \AgilePayments\Ump\Util::curlPost($url, ['appId' => $this->appId, 'timestamp' => $timestamp, 'nonce' => $nonce, 'signMethod' => $signMethod, 'signature' => $signature]);
            if ($result['errCode'] === '0000') {
                $redis->set('YSAccessToken',$result['accessToken'],$result['expiresIn']-300);
                $this->accessToken = $result['accessToken'];
            } else {
                $this->accessToken = '';
            }
        }else{
            $this->accessToken = $redis->get('YSAccessToken');
        }
    }

    /**
     * @author zhangZC
     * @creationtime 2022年10月28日
     * @desc 认证数据签名
     */
    private function Signature($timestamp, $nonce): string
    {
        $str = "$this->appId$timestamp$nonce$this->appKey";
        return hash('SHA256',$str);
    }

    /**
     * 支付
     * @param string    $notify_url
     * @param string    $order_no
     * @param int       $pay_price
     * @param string    $out_time
     * @param string    $extra_param
     * @return void
     */
    public function pay($order_no, $pay_price, $out_time, $notify_url, $extra_param, $title = '')
    {
        $url = $this->serverBaseUrl.'/v1/netpay/trade/app-pre-order';   // 支付宝小程序支付
        $data = [
            "requestTimestamp"  => date('Y-m-d H:i:s'),
            "tid"               => $this->config->pay_type_config->tid,
            "mid"               => $this->config->pay_type_config->mid,
            "targetAppScheme"   => "ccnbpay",
            "expireTime"        => date('Y-m-d H:i:s',$out_time),
            "notifyUrl"         => $notify_url,
            "subAppId"          => $this->config->pay_type_config->sub_app_id,
            "attachedData"      => json_encode($extra_param),
            "instMid"           => "APPDEFAULT",
            "merOrderId"        => $order_no,
            "msgId"             => $order_no,
            "totalAmount"       => $pay_price,
            "tradeType"         => "APP",
        ];

        Tool::log($data);
        $result = $this->curlPost($url, $data, 'json', ['Authorization: OPEN-ACCESS-TOKEN AccessToken="' . $this->accessToken . '"']);
        Tool::log($result);
        if ($result['errCode'] == 'SUCCESS') {
            return ['status' => true, 'data'=>['appPayRequest' => $result['appPayRequest'], 'pay_type' => $this->config->pay_type], 'msg' => $result['errCode'], 'code' => 1,'time'=>time()];
        } else {
            return ['status' => false, 'data'=>['appPayRequest' => [], 'pay_type' => $this->config->pay_type], 'msg' => $result['errCode'], 'code' => 0,'time'=>time()];
        }
    }


    public function query($trade_no)
    {
        $url = $this->serverBaseUrl . 'v1/netpay/query';
        $data = [
            "requestTimestamp"  => date('Y-m-d H:i:s'),
            "tid"               => $this->config->pay_type_config->tid,
            "mid"               => $this->config->pay_type_config->mid,
            "merOrderId"        => $trade_no,
            "instMid"           => "APPDANDEFAULT",
        ];
        $result = $this->curlPost($url,$data,'json',['Authorization: OPEN-ACCESS-TOKEN AccessToken="'.$this->accessToken.'"']);
        if ($result['errCode'] == 'SUCCESS'){
            return ['status'=>true, 'msg'=>$result['errMsg'],'data'=>$result];
        }else{
            return ['status'=>false, 'msg'=>$result['errMsg'],'data'=>$result];
        }
    }

    public function refund($payInfo, $notify = '')
    {
        $url = $this->serverBaseUrl . 'v1/netpay/refund';
        $data = [
            "requestTimestamp"  => date('Y-m-d H:i:s'),
            "tid"               => $this->config->pay_type_config->tid,
            "mid"               => $this->config->pay_type_config->mid,
            "merOrderId"        => $payInfo['out_order_id'],
            "instMid"           => "APPDANDEFAULT",
            "refundAmount"      => $payInfo['pay_amount'],
        ];
        $result = $this->curlPost($url,$data,'json',['Authorization: OPEN-ACCESS-TOKEN AccessToken="'.$this->accessToken.'"']);
        if ($result['errCode'] == 'SUCCESS' && $result['refundStatus'] == 'SUCCESS'){
            return ['status'=>true, 'msg'=>$result['errMsg'],'data'=>$result];
        }else{
            return ['status'=>false, 'msg'=>$result['errMsg'],'data'=>$result];
        }
    }


    public function refundQuery($refund_no, $trade_no, $date = '')
    {
        $url = $this->serverBaseUrl . 'v1/netpay/refund-query';
        $data = [
            "requestTimestamp"  => date('Y-m-d H:i:s'),
            "tid"               => $this->config->pay_type_config->tid,
            "mid"               => $this->config->pay_type_config->mid,
            "merOrderId"        => $refund_no,
            "instMid"           => "APPDEFAULT",
        ];

        $result = $this->curlPost($url,$data,'json',['Authorization: OPEN-ACCESS-TOKEN AccessToken="'.$this->accessToken.'"']);
        if ($result['refundStatus'] == 'SUCCESS'){
            return [
                'status'    => true,
                'msg'       => $result['errMsg'],
                'data'      => $result,
            ];
        }else{
            return [
                'status'    => false,
                'msg'       => $result['errMsg'],
                'data'      => $result,
            ];
        }
    }






    /**
     * @author zhangZC
     * @creationtime  2022年11月2日
     * @desc 返回外部订单号除头部4位以外的部分
     * @return String
     */
    private function outOrderId () : String
    {
        return substr(date_format(date_create(),"YmdHisu"),0,-3).Random::numeric(7);
    }

    /**
     * @author zhangZC
     * @creationtime 2022年10月28日
     * @desc Post->json 请求
     */
    private function curlPost($url,$params = [], $type = 'json', $header = [])
    {
        $head = [];
        if ($type == 'json'){
            $head = ['Content-Type: application/json'];
        }
        foreach ($header as $v){
            $head[] = $v;
        }
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
        // POST数据
        curl_setopt($ch, CURLOPT_POST, 1);
        // 把post的变量加上
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($params));
        curl_setopt($ch, CURLOPT_HTTPHEADER, $head);
        $output = curl_exec($ch);
        curl_close($ch);
        return json_decode($output,true);

    }


    public function verifySign($request)
    {
        $params = array_map(function($value){// 强制更新代码块
            return urldecode($value);// 强制更新代码块
        }, $_POST);// 强制更新代码块
        $sign = $this->makeSign($params);
        $notifySign = array_key_exists('sign', $params) ? $params['sign'] : '';
        if (strcmp($sign, $notifySign) == 0) {
            return [
                'status'        => true,
                'payOrderId'    => $_POST['merOrderId'],
                'trade_no'      => $_POST['targetOrderId']??'',
                'query_id'      => $_POST['queryId']??'',
                'data'          => $_POST??[],
            ];
        } else {
            return false;
        }
    }

    public function makeSign($params) {
        $str = $this->buildSignStr($params) .  $this->config->pay_type_config->app_secret;
        if($params['signType']=='SHA256'){
            return strtoupper(hash('sha256',$str));
        }
        return strtoupper(hash('md5',$str));
    }
    public function buildSignStr($params) {
        $keys = [];
        foreach($params as $key => $value) {
            if ($key == 'sign' || is_null($value)) {
                continue;
            }
            array_push($keys, $key);
        }
        $str = '';
        sort($keys);
        $len = count($keys);
        for($i = 0; $i < $len; $i++) {
            $v = $params[$keys[$i]];
            if (is_array($v)) {
                $v = json_encode($v);
            }
            $str .= $keys[$i] . '=' . $v . (($i === $len -1) ? '' : "&");
        }
        return $str;
    }

    public function returnSuccess()
    {
        echo "SUCCESS";
    }
}